#include <BulletSoftBody/btSoftBodyRigidBodyCollisionConfiguration.h>
#include <BulletSoftBody/btSoftRigidDynamicsWorld.h>
#include <btBulletDynamicsCommon.h>
#include <BulletDynamics\Dynamics\btRigidBody.h>
#include <BulletSoftBody\btSoftBodyHelpers.h>
#include <BulletCollision/CollisionDispatch/btGhostObject.h>

#include <OGRE\OgreMaterialManager.h>
#include <OGRE\OgreSceneManager.h>
#include <OGRE\OgreConfigFile.h>
#include <OGRE\OgreRoot.h>
#include <OGRE\OgreEntity.h>
#include <OGRE\OgreSubMesh.h>
#include <OGRE\OgreManualObject.h>

#include "PlayState.hpp"
#include "BtOgre\BtOgreExtras.h"
#include "Action.hpp"
#include "ObjectTextDisplay.hpp"

class NextLevelAction : public Action
{
	public:
		NextLevelAction(Level* levelController) : m_pLevelController(levelController), m_lastTime(0), m_visited(0) {}

		virtual const char* getName() { return "NextLevel"; }

		virtual void process()
		{
			
			//check when we last called this action to prevent chain level switching from happening
			//we will also allow level switching if this is the first time it occurs no matter
			//the timing
			if ((m_Timer.getMillisecondsCPU() - m_lastTime) > 2000 || m_visited == 0)
			{
				m_visited++;
				m_lastTime = m_Timer.getMillisecondsCPU();
				//let's perform the next level action
				//m_pLevelController->nextLevel();
				Ogre::LogManager::getSingleton().logMessage("NextLevelAction processed!!!");
			}
		}
	private:
		Ogre::Timer m_Timer;
		int m_visited;
		double m_lastTime;
		Level* m_pLevelController;
};

class DeathAction : public Action
{
	public:
		DeathAction(PlayState* ps, std::list<Action*>* al) : m_TimeElapsed(0), m_PS(ps), m_ActionList(al) {}

		virtual const char* getName() { return "DeathAction"; }

		virtual void process()
		{
			//go back to menu since we died... buhu
			m_TimeElapsed = 0;
			m_otd.enable(true);
			m_otd.setText("Game Over");
			m_ActionList->push_back(this);
		}

		virtual void update(double dt)
		{
			if (m_TimeElapsed > 10000)
			{
				//m_otd.enable(false);
				m_otd.setText("");
				m_ActionList->remove(this);
				m_PS->ChangeState(0);
				return;
			}

			m_otd.update();
			m_TimeElapsed += dt;
		}

	private:
		ObjectTextDisplay m_otd;
		double m_TimeElapsed;
		PlayState* m_PS;
		std::list<Action*>*		m_ActionList;
};

/*
	We require a custom near callback routine for bullet to handle ghost objects the way we want
	If we don't do this, ghost objects will act as invisible obstacles
*/
void CustomNearCallback( btBroadphasePair& collisionPair, btCollisionDispatcher& dispatcher, const btDispatcherInfo& dispatchInfo)
{
	btCollisionObject* colObj0 = (btCollisionObject*) collisionPair.m_pProxy0->m_clientObject;
	btCollisionObject* colObj1 = (btCollisionObject*) collisionPair.m_pProxy1->m_clientObject;
	btGhostObject* ghost0 = btGhostObject::upcast(colObj0);
	btGhostObject* ghost1 = btGhostObject::upcast(colObj1);

	if (!ghost0 && !ghost1)
	{
		// ghosts will not affect other rigidbodies
		dispatcher.defaultNearCallback(collisionPair, dispatcher, dispatchInfo); 
	} else
	{
		/*
			Note: this section has to remain empty for the ghost objects to react as we want
			the comment below has been left in because that's part of how the original
			code should look, but we don't want to use it...
		*/
		// add your ghost overlap callbacks here so ghostObjects can 
		// ghost(0/1)->addOverlappingObjectInternal(proxy1, proxy0);
	}
}

void bulletTickCallback(btDynamicsWorld* world, btScalar timestep)
{
	Character* pCharacter = static_cast<Character*>(world->getWorldUserInfo());
	btVector3 velocity;

	if (pCharacter == 0)
		return;

	const int maxSpeed = 1;

	switch (pCharacter->getState())
	{
		case CS_SOLID:
			velocity = pCharacter->m_SolidGhostObject->getInterpolationLinearVelocity();
			if (velocity.length() > maxSpeed)
			{
				Ogre::LogManager::getSingleton().logMessage("SOLID moving too fast");
				velocity *= maxSpeed / velocity.length();
				pCharacter->m_SolidGhostObject->setInterpolationLinearVelocity(velocity);
			}
			break;
		case CS_LIQUID:
			velocity = pCharacter->m_LiquidBody->getInterpolationLinearVelocity();
			if (velocity.length() > maxSpeed)
			{
				Ogre::LogManager::getSingleton().logMessage("LIQUID moving too fast");
				velocity *= maxSpeed / velocity.length();
				pCharacter->m_LiquidBody->setInterpolationLinearVelocity(velocity);
			}
			break;
		case CS_GAS:
			velocity = pCharacter->m_GasBody->getInterpolationLinearVelocity();
			if (velocity.length() > maxSpeed)
			{
				Ogre::LogManager::getSingleton().logMessage("GAS moving too fast");
				velocity *= maxSpeed / velocity.length();
				pCharacter->m_GasBody->setInterpolationLinearVelocity(velocity);
			}
			break;
	}

}

void PlayState::Init()
{
	char msg[255];

	Ogre::LogManager::getSingletonPtr()->logMessage("Initiating PlayState...");

	m_pScene = Ogre::Root::getSingletonPtr()->createSceneManager(Ogre::ST_GENERIC, "PlaySceneManager");
	m_pScene->setAmbientLight(Ogre::ColourValue(0,0,0));
	/*
	m_pCameraYawNode = m_pScene->getRootSceneNode()->createChildSceneNode("PlayCamYawNode", Ogre::Vector3(0, 35, 50));
	m_pCameraNode = m_pCameraYawNode->createChildSceneNode("PlayCamPitchNode");

	m_pCamera = m_pScene->createCamera("PlayCamPitch");
	m_pCameraNode->attachObject(m_pCamera);

	//m_pCameraYawNode->setPosition(Ogre::Vector3(0, 25, -50));
	m_pCamera->lookAt(Ogre::Vector3(0, 20, 0));
	m_pCamera->setNearClipDistance(5);
	m_pCamera->setFarClipDistance(1000);
	m_pCamera->setAspectRatio( Ogre::Real(m_pViewport->getActualWidth()) / Ogre::Real(m_pViewport->getActualHeight()) );
	m_pViewport->setCamera(m_pCamera);

	*/
	//-----------Sinbad Cam---------------
	m_pCamera = m_pScene->createCamera("PlayCamPitch");
	// create a pivot at roughly the character's shoulder
	mCameraPivot = m_pCamera->getSceneManager()->getRootSceneNode()->createChildSceneNode("PlayCamYawNode", Ogre::Vector3(0, 35, 50));
	// this is where the camera should be soon, and it spins around the pivot
	mCameraGoal = mCameraPivot->createChildSceneNode(Ogre::Vector3(0, 0, 15));
	// this is where the camera actually is
	m_pCameraNode = m_pCamera->getSceneManager()->getRootSceneNode()->createChildSceneNode("PlayCamPitchNode");
	m_pCameraNode->setPosition(mCameraPivot->getPosition() + mCameraGoal->getPosition());

	mCameraPivot->setFixedYawAxis(true);
	mCameraGoal->setFixedYawAxis(true);
	m_pCameraNode->setFixedYawAxis(true);
	// our model is quite small, so reduce the clipping planes
	m_pCamera->setNearClipDistance(0.1);
	m_pCamera->setFarClipDistance(200);
	m_pCameraNode->attachObject(m_pCamera);

	mPitch = 0;
	m_pCamera->setAspectRatio( Ogre::Real(m_pViewport->getActualWidth()) / Ogre::Real(m_pViewport->getActualHeight()) );
	m_pViewport->setCamera(m_pCamera);
	//------------------------------------



	//m_pGUIRenderer->setTargetSceneManager(m_pSceneMgr);

#ifdef GAME_PROFILE
	Ogre::Timer timer;
	double time1 = timer.getMilliseconds();
	double time2 = time1;
#endif

	/*
		Setup the physics world now
	*/

	//we need a specific version of a collision configuration to handle both soft and rigid bodies together
	//to see what each of these do check the bullet documentation, they are all required by the physics engine
	m_pCollisionConfig = new btSoftBodyRigidBodyCollisionConfiguration();
	m_pDispatcher = new btCollisionDispatcher(m_pCollisionConfig);
	m_pBroadphase = new btDbvtBroadphase();
	m_pSolver = new btSequentialImpulseConstraintSolver();

	//###############################################################################################################################
#ifdef GAME_PROFILE
	time1 = timer.getMilliseconds();'
#endif
	//###############################################################################################################################

	btSoftRigidDynamicsWorld* world = new btSoftRigidDynamicsWorld(m_pDispatcher, m_pBroadphase, m_pSolver, m_pCollisionConfig);
	world->getDispatchInfo().m_useContinuous = true;
	world->setGravity(btVector3(0, -10, 0));
	m_sbWorldInfo.m_gravity.setValue(0, -10, 0);
	
	m_pDynamicsWorld = world;
	

	m_pDispatcher->setNearCallback(CustomNearCallback);
	m_pDynamicsWorld->getBroadphase()->getOverlappingPairCache()->setInternalGhostPairCallback(new btGhostPairCallback());
	//create the debug drawer (showing wireframe for collision data) and set it as the current debug drawer
	//to actually make it draw anything we have to call the step() method each frame as well
	m_pBtDebugDrawer = new BtOgre::DebugDrawer(m_pScene->getRootSceneNode()->createChildSceneNode(), m_pDynamicsWorld);
	m_pDynamicsWorld->setDebugDrawer(m_pBtDebugDrawer);
	m_pBtDebugDrawer->setDebugMode(0);

	m_sbWorldInfo.m_dispatcher = m_pDispatcher;
	m_sbWorldInfo.m_broadphase = m_pBroadphase;
	m_sbWorldInfo.m_sparsesdf.Initialize();
	//m_sbWorldInfo.m_sparsesdf.Reset();

	//###############################################################################################################################
#ifdef GAME_PROFILE
	time2 = timer.getMilliseconds();
	sprintf(msg, "TIMING physics initialization: %f", time2 - time1);
	Ogre::LogManager::getSingleton().logMessage(msg);
#endif
	//###############################################################################################################################

	//###############################################################################################################################
#ifdef GAME_PROFILE
	time1 = timer.getMilliseconds();
#endif
	//###############################################################################################################################

	Ogre::SceneNode* levelNode = m_pScene->getRootSceneNode()->createChildSceneNode("LevelNode");
	m_pLevelController = new Level(m_pDynamicsWorld, m_pScene, levelNode, &m_EM);

	//###############################################################################################################################
#ifdef GAME_PROFILE
	time2 = timer.getMilliseconds();
	sprintf(msg, "TIMING level controller initialization: %f", time2 - time1);
	Ogre::LogManager::getSingleton().logMessage(msg);
#endif
	//###############################################################################################################################

	m_pScene->setShadowTechnique(Ogre::SHADOWTYPE_STENCIL_ADDITIVE);


	//###############################################################################################################################
#ifdef GAME_PROFILE
	time1 = timer.getMilliseconds();
#endif
	//###############################################################################################################################

	/*
		read the game configuration file game.cfg and parse it
	*/
	BlobShapeParams bsp;
	readGameConfiguration(bsp);

	m_pLevelController->changeLevel("Tutorial");
	//###############################################################################################################################
#ifdef GAME_PROFILE
	time2 = timer.getMilliseconds();
	sprintf(msg, "TIMING changeLevel to Tutorial: %f", time2 - time1);
	Ogre::LogManager::getSingleton().logMessage(msg);
#endif
	//###############################################################################################################################

	//###############################################################################################################################
#ifdef GAME_PROFILE
	time1 = timer.getMilliseconds();
#endif
	//###############################################################################################################################

	/*
		Initialize the character's three states
		We need to have a level selected already for this to work, because
		it fetches the currently selected levels character starting position
	*/

	
	initCharacter(bsp);
	
	initListeners();

	//###############################################################################################################################
#ifdef GAME_PROFILE
	time2 = timer.getMilliseconds();
	sprintf(msg, "TIMING character initialization: %f", time2 - time1);
	Ogre::LogManager::getSingleton().logMessage(msg);
#endif
	//###############################################################################################################################
	
	m_pDynamicsWorld->setInternalTickCallback(bulletTickCallback, static_cast<void*>(m_pCharacter), true);

	SetInitialized(true);
}

void PlayState::Exit()
{

	for (int i = 0; i < m_CollisionShapes.size(); i++)
	{
		delete m_CollisionShapes[i];
	}
	m_CollisionShapes.clear();

	delete m_pDynamicsWorld;
	delete m_pSolver;
	delete m_pBroadphase;
	delete m_pDispatcher;
	delete m_pCollisionConfig;


	delete m_pCharacter;

	SetInitialized(false);
}

#include <iostream>
void PlayState::Run(double dt)
{
	char msg[200];

	if (!m_Paused)
	{
		Ogre::Vector3 walkDirection = Ogre::Vector3::ZERO;
		Ogre::Vector3 forwardVector = m_pCamera->getDerivedDirection();
		Ogre::Vector3 rightVector = m_pCamera->getDerivedRight();
		forwardVector.normalise();
		rightVector.normalise();

		if (m_pKeyboard->isKeyDown(OIS::KC_A))
			walkDirection -= rightVector;
		if (m_pKeyboard->isKeyDown(OIS::KC_D))
			walkDirection += rightVector;
		if (m_pKeyboard->isKeyDown(OIS::KC_W))
			walkDirection += forwardVector;
		if (m_pKeyboard->isKeyDown(OIS::KC_S))
			walkDirection -= forwardVector;

		walkDirection.normalise();

		switch (m_pCharacter->getState())
		{
			case CS_SOLID:
				walkDirection = walkDirection * 4 * (dt / 1000);
				break;
			case CS_GAS: 
				walkDirection = walkDirection * 4;
				break;
			case CS_LIQUID:
				walkDirection = walkDirection * 10;
				break;
		}
		
		/*

	if (m_pKeyboard->isKeyDown(OIS::KC_A))
		m_pCharacter->applyForce(leftVector.x, leftVector.y, leftVector.z, dt);
	if (m_pKeyboard->isKeyDown(OIS::KC_D))
		m_pCharacter->applyForce(rightVector.x, rightVector.y, rightVector.z, dt);
	if (m_pKeyboard->isKeyDown(OIS::KC_W))
	{
		m_pCharacter->applyForce(forwardVector.x, forwardVector.y, forwardVector.z, dt);
		m_pCharacter->setBaseAnimation(ANIM_RUN_BASE, true);
		if (m_pCharacter->mTopAnimID == ANIM_IDLE_TOP) 
			m_pCharacter->setTopAnimation(ANIM_RUN_TOP, true);
	}
	if (m_pKeyboard->isKeyDown(OIS::KC_S))
		m_pCharacter->applyForce(backVector.x, backVector.y, backVector.z, dt);
		*/


		if (m_pCharacter->isFalling())
		{
			Ogre::LogManager::getSingleton().logMessage("Character falling. Compensating!");
			/*forwardVector = forwardVector * 0.1;
			rightVector = rightVector * 0.1;*/
			walkDirection = walkDirection * 0.06;
		}
		
		m_pCharacter->applyForce(walkDirection.x, walkDirection.y, walkDirection.z);
		

		if (!m_pKeyboard->isKeyDown(OIS::KC_A) && !m_pKeyboard->isKeyDown(OIS::KC_D) && !m_pKeyboard->isKeyDown(OIS::KC_S) && !m_pKeyboard->isKeyDown(OIS::KC_W))
		{
			//m_pCharacter->applyForce(0.0, 0.0, 0.0, dt);
			m_pCharacter->setBaseAnimation(ANIM_IDLE_BASE);
			if (m_pCharacter->mTopAnimID == ANIM_RUN_TOP) 
				m_pCharacter->setTopAnimation(ANIM_IDLE_TOP);
		}

		updateCamera(dt);

		//move the physics simulation ahead one timestep
		//dt is divided to move it into seconds since Bullet expects that
		m_pDynamicsWorld->stepSimulation(dt / 1000.f, 7);
		m_sbWorldInfo.m_sparsesdf.GarbageCollect();
		m_pCharacter->update(dt, m_pCameraNode, forwardVector);

		m_pLevelController->update(dt);

		/*
		sprintf(msg, "Character node pos: %f %f %f", m_pScene->getSceneNode("CharacterNode")->getPosition().x, m_pScene->getSceneNode("CharacterNode")->getPosition().y, m_pScene->getSceneNode("CharacterNode")->getPosition().z);
		Ogre::LogManager::getSingleton().logMessage(msg);*/


		
		m_pBtDebugDrawer->step();
	}
}

void PlayState::updateCamera(float deltaTime)
{

	//---------------------Ogre Cam----------------------------------

	// place the camera pivot roughly at the character's shoulder
	mCameraPivot->setPosition(m_pScene->getSceneNode("CharacterNode")->getPosition() + Ogre::Vector3::UNIT_Y);
	// move the camera smoothly to the goal
	Ogre::Vector3 goalOffset = mCameraGoal->_getDerivedPosition() - m_pCamera->getDerivedPosition();

	//--Debugg--
	/*
	Ogre::Vector3 vCharacterNode = m_pScene->getSceneNode("CharacterNode")->getPosition();
	Ogre::Vector3 preLoggVcameraNode = m_pCameraNode->getPosition();
	char nodePoss[200], goalPoss[200], cdeltaTime[200], prenodePoss[200], cgoalOffset[200], cCharacterNode[200];
	sprintf(cCharacterNode, "CharacterNode:	 << x: %f y: %f z: %f >>",vCharacterNode.x,vCharacterNode.y,vCharacterNode.z);
	Ogre::LogManager::getSingletonPtr()->logMessage(cCharacterNode);
	sprintf(cgoalOffset, "goalOffset		 << x: %f y: %f z: %f >>",goalOffset.x,goalOffset.y,goalOffset.z);
	Ogre::LogManager::getSingletonPtr()->logMessage(cgoalOffset);
	sprintf(prenodePoss, "Prior to Translate << x: %f y: %f z: %f >>",preLoggVcameraNode.x,preLoggVcameraNode.y,preLoggVcameraNode.z);
	Ogre::LogManager::getSingletonPtr()->logMessage(prenodePoss);
	
	*///------------
	m_pCameraNode->translate(goalOffset * (deltaTime/1000) * 9.0f);
	// always look at the pivot
	m_pCameraNode->lookAt(mCameraPivot->_getDerivedPosition(), Ogre::Node::TS_WORLD);
	Ogre::Vector3 loggVcameraNode = m_pCameraNode->getPosition();
	Ogre::Vector3 loggVcameraGoal = mCameraGoal->_getDerivedPosition();
	
	//  <<<Debugg>>>
	/*
	sprintf(cdeltaTime,  "<<<<deltaTime:   %f >>>>>",deltaTime);
	Ogre::LogManager::getSingletonPtr()->logMessage(cdeltaTime);
	sprintf(nodePoss,    "Camera Poss:		 << x: %f y: %f z: %f >>",loggVcameraNode.x,loggVcameraNode.y,loggVcameraNode.z);
	Ogre::LogManager::getSingletonPtr()->logMessage(nodePoss);
	sprintf(goalPoss,    "-------Camera Goal << x: %f y: %f z: %f >>--------------",loggVcameraGoal.x,loggVcameraGoal.y,loggVcameraGoal.z);
	Ogre::LogManager::getSingletonPtr()->logMessage(goalPoss);
	*/
	//<<<End of Debugg>>>

}

void PlayState::updateCameraGoal(float deltaYaw, float deltaPitch, float deltaZoom)
{
	mCameraPivot->yaw(Ogre::Degree(deltaYaw), Ogre::Node::TS_WORLD);
		// bound the pitch
	if (!(mPitch + deltaPitch > 50 && deltaPitch > 0) &&
		!(mPitch + deltaPitch < -60 && deltaPitch < 0))
	{
		mCameraPivot->pitch(Ogre::Degree(deltaPitch), Ogre::Node::TS_LOCAL);
		mPitch += deltaPitch;
	}
	
	float dist = mCameraGoal->_getDerivedPosition().distance(mCameraPivot->_getDerivedPosition());
	float distChange = deltaZoom * dist;

	// bound the zoom
	if (!(dist + distChange < 8 && distChange < 0) &&
		!(dist + distChange > 25 && distChange > 0))
	{
		mCameraGoal->translate(0, 0, distChange, Ogre::Node::TS_LOCAL);
	}
}

void PlayState::Pause()
{
	m_Paused = true;
	Ogre::LogManager::getSingletonPtr()->logMessage("Pausing PlayState...");
}

void PlayState::Resume()
{
	m_Paused = false;
	Ogre::LogManager::getSingletonPtr()->logMessage("Resuming PlayState...");

	m_pViewport->setCamera(m_pCamera);
	m_pViewport->update();
	//m_pGUIRenderer->setTargetSceneManager(m_pSceneMgr);
	//m_pGUISystem->setGUISheet(CEGUI::WindowManager::getSingleton().getWindow("name of gui"));
}

/*
	input event callbacks

*/
bool PlayState::keyPressed(const OIS::KeyEvent &keyEventRef)
{
	//temporary test key for switching levels
	if (keyEventRef.key == OIS::KC_G)
	{
		m_pLevelController->nextLevel();
		double x,y,z;
		m_pLevelController->getStartPos(x, y, z);
		m_pCharacter->setPosition( btVector3(x, y, z) );
	}
	if (keyEventRef.key == OIS::KC_F12)
	{
		ChangeState(0);
	}
	return true;
}

bool PlayState::keyReleased(const OIS::KeyEvent &keyEventRef)
{
	char msg[200];
	if (keyEventRef.key == OIS::KC_B)
	{
		m_ShowDebugWorld = !m_ShowDebugWorld;
		sprintf(msg, "Show debug: %d",  (m_ShowDebugWorld == true) ? 1 : 0);
		Ogre::LogManager::getSingleton().logMessage(msg);

		m_pBtDebugDrawer->setDebugMode( (m_ShowDebugWorld == true) ? 1 : 0);
	}

	if (keyEventRef.key == OIS::KC_1)
		m_pCharacter->changeState(CS_SOLID);
	if (keyEventRef.key == OIS::KC_2)
		m_pCharacter->changeState(CS_GAS);
	if (keyEventRef.key == OIS::KC_3)
		m_pCharacter->changeState(CS_LIQUID);

	return true;
}

bool PlayState::mouseMoved(const OIS::MouseEvent &evt)
{
	updateCameraGoal(-0.05f * evt.state.X.rel, -0.05f * evt.state.Y.rel, -0.0005f * evt.state.Z.rel);
	return true;
}

bool PlayState::mousePressed(const OIS::MouseEvent &evt, OIS::MouseButtonID id)
{

	return true;
}

bool PlayState::mouseReleased(const OIS::MouseEvent &evt, OIS::MouseButtonID id)
{
	return true;
}

void PlayState::reset()
{
	m_pLevelController->resetCurrentLevel();
	double x,y,z;
	m_pLevelController->getStartPos(x, y, z);
	m_pCharacter->setPosition( btVector3(x, y, z) );
}

void PlayState::readGameConfiguration(BlobShapeParams& bsp)
{
	char msg[255]; // write debug messages into this array and then log them using Ogre

	//set default values for the liquid body parameters
	bsp.kpr = 3500.f;
	bsp.kdp = 0.001f;
	bsp.kdf = 0.1f;
	bsp.mklst = 0.1f;

	/*
		load game configuration
		will overwrite defaults if the value exists in the file
	*/

	Ogre::ConfigFile liquidcfg;
	//this means that we look for game.cfg from the current working directory
	liquidcfg.load("game.cfg");

	Ogre::String sectionName, optionName, value;
	Ogre::ConfigFile::SectionIterator seci = liquidcfg.getSectionIterator();
	while (seci.hasMoreElements())
	{
		sectionName = seci.peekNextKey();
		Ogre::ConfigFile::SettingsMultiMap *settings = seci.getNext();
		Ogre::ConfigFile::SettingsMultiMap::iterator i;

		if (sectionName == "Blob properties")
		{
			for (i = settings->begin(); i != settings->end(); ++i)
			{
				optionName = i->first;
				value = i->second;
				if (optionName == "kpr")
					bsp.kpr  = atoi(value.c_str());
				if (optionName == "kdp")
					bsp.kdp = atof(value.c_str());
				if (optionName == "kdf")
					bsp.kdf = atof(value.c_str());
				if (optionName == "klst")
					bsp.mklst = atof(value.c_str());
			}
		//read in a list of all levels defined for the game
		} else if (sectionName.substr(0, 5) == "Level")
		{
			LevelInfo newLevel;
			newLevel.title = "!invalid";
			newLevel.scenefile = "!invalid";
			newLevel.nextlevel = "";
			for (i = settings->begin(); i != settings->end(); ++i)
			{
				optionName = i->first;
				value = i->second;
				if (optionName == "title")
					newLevel.title = value;
				if (optionName == "nextlevel")
					newLevel.nextlevel = value;
				if (optionName == "scenefile")
					newLevel.scenefile = value;
				if (optionName == "startpos")
				{
					int pos = value.find_first_of(",");
					newLevel.startX = atof(value.substr(0, pos).c_str());
					int pos2 = value.find(",", pos + 1);
					newLevel.startY = atof(value.substr(pos + 1, pos2).c_str());
					pos = value.find(",", pos2 + 1);
					newLevel.startZ = atof(value.substr(pos2 + 1, pos).c_str());
					sprintf(msg, "charstartpos: %f, %f, %f", newLevel.startX, newLevel.startY, newLevel.startZ);
					Ogre::LogManager::getSingleton().logMessage(msg);
				}
			}

			if (newLevel.title != "!invalid" && newLevel.scenefile != "!invalid")
				m_pLevelController->add(newLevel);
		}
	}
}

void PlayState::initCharacter(const BlobShapeParams &bsp)
{
	double x, y, z;
	m_pLevelController->getStartPos(x, y, z);
	Ogre::Vector3 characterStartPos(x, y, z);

	//characterStartPos = Ogre::Vector3(m_Levels[0].startX, m_Levels[0].startY, m_Levels[0].startZ);
	m_pCharacter = new Character(m_pScene, (btSoftRigidDynamicsWorld*)m_pDynamicsWorld, &m_sbWorldInfo);
	
	//the scene node is used to attach the characters visual representation so we can show it
	Ogre::SceneNode* characterNode = m_pScene->getRootSceneNode()->createChildSceneNode("CharacterNode", characterStartPos);
	m_pScene->getRootSceneNode()->createChildSceneNode("CharacterBlobNode", Ogre::Vector3(0,0,0));
	/*
	//--Sinbad Stuff--
	Ogre::Entity* mBodyEnt = m_pScene->createEntity("SinbadBody", "Sinbad.mesh");
	// create swords and attach to sheath
	Ogre::Entity* mSword1 = m_pScene->createEntity("SinbadSword1", "Sword.mesh");
	Ogre::Entity* mSword2 = m_pScene->createEntity("SinbadSword2", "Sword.mesh");
	mBodyEnt->attachObjectToBone("Sheath.L", mSword1);
	mBodyEnt->attachObjectToBone("Sheath.R", mSword2);

	m_pScene->getSceneNode("CharacterNode")->
	*/
	//---------------

#ifdef GAME_PROFILE
	Ogre::Timer timer;
	double time1, time2;
#endif
	char msg[200];

	/*
		SOLID STATE
		create the shape and rigid body
	*/
	//###############################################################################################################################
#ifdef GAME_PROFILE
	time1 = timer.getMilliseconds();
#endif
	//###############################################################################################################################
	/*btCollisionShape* solidShape = new btSphereShape(5);
	m_CollisionShapes.push_back(solidShape);
	btDefaultMotionState* motionState = new btDefaultMotionState(btTransform( btQuaternion(0,0,0,1), btVector3(characterStartPos.x, characterStartPos.y, characterStartPos.z) ));
	btRigidBody::btRigidBodyConstructionInfo rbInfo2(10, motionState, solidShape, btVector3(0,0,0));
	
	
	m_pCharacter->m_SolidBody = new btRigidBody(rbInfo2);*/
	//m_pCharacter->m_SolidEntity = m_pScene->createEntity(Ogre::SceneManager::PrefabType::PT_SPHERE);
	m_pCharacter->m_SolidEntity = m_pScene->createEntity("SinbadBody", "Sinbad.mesh");
	//###############################################################################################################################
#ifdef GAME_PROFILE
	time2 = timer.getMilliseconds();
	sprintf(msg, "TIMING Character SOLID state: %f", time2 - time1);
	Ogre::LogManager::getSingleton().logMessage(msg);
#endif
	//###############################################################################################################################

	/*
		BLOB/LIQUID STATE
		create the soft body for the liquid/blob form of the character
		no collision shape is made in this case, because that is taken care of automatically by btSoftBody
	*/
	//###############################################################################################################################
#ifdef GAME_PROFILE
	time1 = timer.getMilliseconds();
#endif
	//###############################################################################################################################
	
	m_pCharacter->createBlobForm(bsp, btVector3(characterStartPos.x, characterStartPos.y, characterStartPos.z));

	//###############################################################################################################################
#ifdef GAME_PROFILE
	time2 = timer.getMilliseconds();
	sprintf(msg, "TIMING Character BLOB state: %f", time2 - time1);
	Ogre::LogManager::getSingleton().logMessage(msg);
#endif
	//###############################################################################################################################
	
	/*
		GAS STATE
		create collision shape and rigid body for the characters gas form
	*/
	//###############################################################################################################################
#ifdef GAME_PROFILE
	time1 = timer.getMilliseconds();
#endif
	//###############################################################################################################################
	btCollisionShape* gasShape = new btSphereShape(2);
	m_CollisionShapes.push_back(gasShape);
	btDefaultMotionState* motionState2 = new btDefaultMotionState(btTransform( btQuaternion(0,0,0,1), btVector3(characterStartPos.x, characterStartPos.y, characterStartPos.z) ));
	btRigidBody::btRigidBodyConstructionInfo rbInfo3(1, motionState2, gasShape, btVector3(0,0,0));
	m_pCharacter->m_GasBody = new btRigidBody(rbInfo3);
	m_pCharacter->m_GasPS = m_pScene->createParticleSystem("GasBody", "Character/GasBody");
	//###############################################################################################################################
#ifdef GAME_PROFILE
	time2 = timer.getMilliseconds();
	sprintf(msg, "TIMING Character GAS state: %f", time2 - time1);
	Ogre::LogManager::getSingleton().logMessage(msg);
#endif
	//###############################################################################################################################

	/*
		run an initial update on the character and choose the starting state
	*/
	//###############################################################################################################################
#ifdef GAME_PROFILE
	time1 = timer.getMilliseconds();
#endif
	//###############################################################################################################################
	m_pCharacter->init();
	m_pCharacter->setPosition( btVector3(characterStartPos.x, characterStartPos.y, characterStartPos.z) );
	m_pCharacter->changeState(CS_SOLID);
	
	
	//###############################################################################################################################
#ifdef GAME_PROFILE
	time2 = timer.getMilliseconds();
	sprintf(msg, "TIMING Character initial UPDATE: %f", time2 - time1);
	Ogre::LogManager::getSingleton().logMessage(msg);
#endif
	//###############################################################################################################################
}

void PlayState::initListeners()
{
	//create next_level event listener to automatically trigger level changing when it occurs
	m_EM.registerListener("next_level", new NextLevelAction(m_pLevelController));
	m_EM.registerListener("kill_player", new DeathAction(this, &m_pLevelController->m_Actions));
}